package com.wu.webflux6.config;

import java.util.LinkedList;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.DelegatingReactiveAuthenticationManager;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;
import org.springframework.security.web.server.authentication.RedirectServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;

import com.wu.webflux6.sms.SmsAuthenticationConverter;
import com.wu.webflux6.sms.SmsReactiveAuthenticationManager;
import com.wu.webflux6.sms.SmsReactiveUserDetailsService;

import reactor.core.publisher.Mono;

@EnableWebFluxSecurity
public class WebFluxConfig {

	@Autowired
	private SmsAuthenticationConverter smsAuthenticationConverter;
	

	@Bean
	public SecurityWebFilterChain springSecurityFilterChain2(ServerHttpSecurity http) {
//		http.securityContextRepository(securityContextRepository)
		ServerHttpSecurity build = http.formLogin().loginPage("/sms")
				.requiresAuthenticationMatcher(
						ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, "/sms/login"))
				.authenticationSuccessHandler(new RedirectServerAuthenticationSuccessHandler("/hello")).and()
				.authorizeExchange().pathMatchers("/js/**", "/actuator/**","/sms/smscode", "/sms", "/logout").permitAll()
				.pathMatchers("/hello").hasAnyAuthority("ROLE_USER").pathMatchers("/**").authenticated().and().logout().and()
				.csrf().disable();
		SecurityWebFilterChain chain = build.build();
		chain.getWebFilters().filter(webFilter -> webFilter instanceof AuthenticationWebFilter).subscribe(webFilter -> {
			AuthenticationWebFilter filter = (AuthenticationWebFilter) webFilter;
			filter.setServerAuthenticationConverter(smsAuthenticationConverter);
		});
		
		return chain;
	}
	
	@Bean
	public ReactiveAuthenticationManager reactiveAuthenticationManager() {
		final SmsReactiveUserDetailsService smsService = new SmsReactiveUserDetailsService();
		MapReactiveUserDetailsService mapReactiveUserDetailsService = new MapReactiveUserDetailsService(User.withDefaultPasswordEncoder()
			      .username("wu")
			      .password("123")
			      .roles("USER","123","456").authorities("ROLE_USER","asd","zxc")
			      .build());
		LinkedList<ReactiveAuthenticationManager> managers = new LinkedList<>();
		managers.add(new SmsReactiveAuthenticationManager(smsService));
		// 必须放最后不然会优先使用用户名密码校验但是用户名密码不对时此 AuthenticationManager 会调用 Mono.error 造成后面的
		// AuthenticationManager 不生效
		managers.add(new UserDetailsRepositoryReactiveAuthenticationManager(mapReactiveUserDetailsService));
		return new DelegatingReactiveAuthenticationManager(managers);

	}
	
	
	@Bean
	public PasswordEncoder initPasswordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	public ReactiveUserDetailsService userDetailsService() {
		return new SmsReactiveUserDetailsService();
	}
}
